/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.wider.foundation.concurrency;

import java.util.ArrayList;
import java.util.List;

/**
 * This class abstracts the Multiple-Buffering scheme to provide users
 * an uniform foundation for constructing Multiple-Buffered such as
 * Double-Buffered, Triple-Buffered applications.
 *
 * @author William Choi <avextk@gmail.com>
 */
public class MultiBuffer {
    public static int BUFF_STATE_READY = 0;
    public static int BUFF_STATE_ACTIVE = 1;
    public static int BUFF_STATE_LOCKED = 2;

    private int buffdepth;
    private int activeidx;
    private int buffcount;

    private ArrayList<Object>[]     buffarray;
    private int[]                   buffstate;

    public MultiBuffer(int multiplicity, int buffer_depth){
        buffarray   = new ArrayList[multiplicity];
        buffstate   = new int[multiplicity];
        buffdepth   = buffer_depth;
        buffcount   = multiplicity;
        activeidx   = 0;
        buffstate[0]= BUFF_STATE_ACTIVE;
    }

    public List getBuffer(){
        List ret = null;

        synchronized (this){
            if (-1 == activeidx){
                throw new IllegalStateException("NO ACTIVE Buffer.");
            }

            if (BUFF_STATE_ACTIVE != buffstate[activeidx]){
                throw new InternalError("Internal index points to NON-ACTIVE buffer.");
            }

            //Create when null
            if (null == buffarray[activeidx])
                buffarray[activeidx] = new ArrayList();

            //Return the active buffer
            ret = buffarray[activeidx];
        }
        
        return ret;
    }

    public boolean lockBuffer(List buff){
        boolean ret = false;

        synchronized (this){

            if (-1 == activeidx){
                throw new IllegalStateException("NO ACTIVE Buffer.");
            }

            if (buff != buffarray[activeidx]){
                throw new IllegalArgumentException("Argument buffer DOESN'T match the ACTIVE buffer.");
            }

            if (BUFF_STATE_ACTIVE != buffstate[activeidx]){
                throw new IllegalArgumentException("Argument buffer is NOT in ACTIVE state.");
            }

            //Lock the ACTIVE buffer
            buffstate[activeidx] = BUFF_STATE_LOCKED;

            //Look for the Next READY buffer to be the new ACTIVE
            int oldidx  = activeidx;
            activeidx   = -1;
            for (int i = (oldidx + 1) % buffcount; i != oldidx; ){
                if (BUFF_STATE_READY == buffstate[i]){
                    buffstate[i] = BUFF_STATE_ACTIVE;
                    activeidx = i;
                    break;
                }
                i = (++i) % buffcount;
            }

            ret = true;
        }

        return ret;
    }

    public boolean releaseBuffer(List buff){
        boolean ret = false;

        synchronized (this){

            //Match the buff argument against all LOCKED buffers
            for (int i = 0; i < buffstate.length; i++){
                if (BUFF_STATE_LOCKED != buffstate[i])
                    continue;

                if (buffarray[i] == buff){
                    if (-1 == activeidx){
                        buffstate[i] = BUFF_STATE_ACTIVE;
                        activeidx = i;
                    }else{
                        buffstate[i] = BUFF_STATE_READY;
                    }
                    ret = true;
                    break;
                }
            }

        }

        if (!ret){
            throw new IllegalArgumentException("Could NOT found the argument buffer.");
        }
        return ret;
    }
}
